package com.yumeng.framework.auth.shiro.filter;

import com.yumeng.common.api.ApiCode;
import com.yumeng.common.auth.BaseAuthInfo;
import com.yumeng.common.data.enums.AuthClientType;
import com.yumeng.common.helper.AssertHelper;
import com.yumeng.common.helper.BaseEnumHelper;
import com.yumeng.common.utils.HttpUtils;
import com.yumeng.common.utils.ResponseUtils;
import com.yumeng.framework.auth.bean.LoginTokenInfo;
import com.yumeng.framework.auth.bean.VerifyLoginInfo;
import com.yumeng.framework.auth.jwt.JwtUtils;
import com.yumeng.framework.auth.service.TokenService;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;

/**
 * 无状态模式过滤器（登录）
 * @author wxd
 * @date 2025/11/18 10:36
 */
@Slf4j
public abstract class AbstractLoginStatelessFilter<T extends AuthenticationToken> extends AbstractStatelessFilter {

    /** 是否json方式接受参数 */
    private boolean jsonParam = false;
    /** 仅支持post方式 */
    private boolean postOnly = true;
    /** 参数名-客户端类型 */
    private String clientTypeParameter = "clientType";

    private final TokenService tokenService;

    public AbstractLoginStatelessFilter(TokenService tokenService){
        this.tokenService = tokenService;
    }

    @Override
    protected AuthenticationToken doCreateToken(ServletRequest request, ServletResponse response) {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (this.postOnly && !HttpUtils.isPost(httpRequest)) {
            throw new AuthenticationException("认证方法不支持该访问方式：" + httpRequest.getMethod());
        }
        String clientTypeStr = null;//客户端类型
        String contentType = request.getContentType();
        T authToken = null;
        if (!this.jsonParam){
            clientTypeStr = WebUtils.getCleanParam(request, clientTypeParameter);
            authToken = obtainAuthenticationToken(httpRequest);
        }else {
            if (!HttpUtils.isJsonMediaType(httpRequest)){
                throw new AuthenticationException("认证参数格式异常：" + contentType);
            }
            //解析请求体中的 JSON 参数
/*            User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
            username = user.getUsername();
            username = (username != null) ? username.trim() : "";
            password = user.getPassword();
            password = (password != null) ? password : "";*/
            clientTypeStr = "待实现";
            authToken = obtainAuthenticationTokenForJson(httpRequest);
        }
        AuthClientType clientType = StringUtils.isNotBlank(clientTypeStr) ?
                BaseEnumHelper.getEnum(AuthClientType.class, Integer.parseInt(clientTypeStr), true) : AuthClientType.getDefault();

        setExtDetails(authToken, clientType);
        return authToken;
    }


    @SneakyThrows
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, VerifyLoginInfo info, ServletRequest request, ServletResponse response) {
        BaseAuthInfo authInfo = (BaseAuthInfo)subject.getPrincipal();
        LoginTokenInfo tokenInfo = null;
        if (authInfo.getDetails() != null && authInfo.getDetails().getAuthClientType() != null){
            tokenInfo = tokenService.generateTokenForLogin(authInfo, JwtUtils.getJwtTypeByAuthClientType(authInfo.getDetails().getAuthClientType()), authInfo.getLoginInfo());
        }
        ResponseUtils.writeJson((HttpServletResponse) response, ApiCode.SUCCESS, tokenInfo,"登录成功");
        //设置标识，说明已经进行了访问拒绝处理，不需要执行onAccessDenied中的逻辑了
        request.setAttribute(getAlreadyAccessDeniedFilteredAttributeName(), Boolean.TRUE);
        return false;
    }

    @SneakyThrows
    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
        log.error("AbstractLoginStatelessFilter::onLoginFailure token:{}, error:{}", token.toString(), e.getMessage());
        //此处要抛出异常，如果只return false，异常将无法被捕捉，前端获取不到具体的异常信息
        throw e;
    }

    public void setJsonParam(boolean jsonParam) {
        this.jsonParam = jsonParam;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public void setClientTypeParameter(String clientTypeParameter) {
        AssertHelper.assertNotBlank(clientTypeParameter, "ClientType parameter must not be empty or null");
        this.clientTypeParameter = clientTypeParameter;
    }

    /**
     * 获取AuthenticationToken
     * @param request
     * @return
     */
    protected abstract T obtainAuthenticationToken(HttpServletRequest request);

    /**
     * 获取AuthenticationToken（json方式）
     * @param request
     * @return
     */
    protected abstract T obtainAuthenticationTokenForJson(HttpServletRequest request);

    /**
     * 设置扩展详细信息
     *
     */
    protected abstract void setExtDetails(T authToken, AuthClientType clientType);
}
